package cn.yunyichina.provider.pay.service;

import cn.yunyichina.provider.iface.entity.alipay.AlipayRequest;
import cn.yunyichina.provider.iface.entity.base.OrgSettingVo;
import cn.yunyichina.provider.iface.entity.base.OrgSettingVoResponse;
import cn.yunyichina.provider.iface.entity.common.ResCommon;
import cn.yunyichina.provider.iface.entity.dto.pacity.PacityPayRequestDTO;
import cn.yunyichina.provider.iface.entity.dto.pay.AlipayBarPayRequestDTO;
import cn.yunyichina.provider.iface.entity.dto.pay.WechatMicroPayRequestDTO;
import cn.yunyichina.provider.iface.entity.dto.wechat.WechatWebPayRequestDTO;
import cn.yunyichina.provider.iface.entity.pay.PayCode;
import cn.yunyichina.provider.pay.config.Alipay;
import cn.yunyichina.provider.pay.config.Config;
import cn.yunyichina.provider.pay.constant.Medicare;
import cn.yunyichina.provider.pay.constant.Pacity;
import cn.yunyichina.provider.pay.entity.PaymentSetting;
import cn.yunyichina.provider.pay.entity.PrescriptionRequestContent;
import cn.yunyichina.provider.pay.entity.RegistrationRequestContent;
import cn.yunyichina.provider.pay.entity.RegistrationRequestContentExt;
import cn.yunyichina.provider.pay.entity.notify.AlipayNotify;
import cn.yunyichina.provider.pay.exception.PaySettingNotFoundException;
import cn.yunyichina.utils.commons.DateUtils;
import cn.yunyichina.utils.commons.RandomUtil;
import cn.yunyichina.utils.constants.Encoding;
import cn.yunyichina.utils.convert.JsonUtils;
import cn.yunyichina.utils.json.Jackson;
import cn.yunyichina.utils.security.SignUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.*;

/**
 * Created by Lullaby on 2016/9/28.
 */
public class BasePayService extends BaseService {

    private static final String LOCALHOST_IP = "127.0.0.1";

    /**
     * 验证微信公众号支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkWechatJsapiPayParameters(Map<String, String> params) {
        if (!CollectionUtils.isEmpty(params)) {
            if (StringUtils.isEmpty(params.get("app_id")) ||
                    StringUtils.isEmpty(params.get("order_no")) ||
                    StringUtils.isEmpty(params.get("pay_amount")) ||
                    StringUtils.isEmpty(params.get("desc")) ||
                    StringUtils.isEmpty(params.get("openid"))) {
                return false;
            }
            return true;
        }

        return false;
    }

    /**
     * 构建微信公众号/小程序支付入参
     *
     * @param params
     * @return
     */
    protected Map<String, String> buildWechatJsapiOrAppletPayParameters(Map<String, String> params) throws PaySettingNotFoundException {
        Map<String, String> result = new HashMap<>();

        PaymentSetting paymentSetting = this.getPaymentSetting(params.get("app_id"));

        result.put("appid", paymentSetting.getPayAppId());
        result.put("mch_id", paymentSetting.getMerchantAccount());
        result.put("nonce_str", RandomUtil.getRandomALLChar(32));
        result.put("body", params.get("desc"));
        result.put("out_trade_no", params.get("order_no"));
        result.put("total_fee", params.get("pay_amount"));
        result.put("spbill_create_ip", LOCALHOST_IP);
        result.put("notify_url", this.getConfig(Config.PAY_WECHAT_NOTIFY_URL));
        result.put("trade_type", "JSAPI");
        result.put("openid", params.get("openid"));
        result.put("api_secret", paymentSetting.getPayKey());
        result.put("attach", buildPayAttachData(params));

        return result;
    }

    /**
     * 验证微信APP支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkWechatAppPayParameters(Map<String, String> params) {
        if (!CollectionUtils.isEmpty(params)) {
            if (StringUtils.isEmpty(params.get("app_id")) ||
                    StringUtils.isEmpty(params.get("order_no")) ||
                    StringUtils.isEmpty(params.get("pay_amount")) ||
                    StringUtils.isEmpty(params.get("desc")) ||
                    StringUtils.isEmpty(params.get("topic"))) {
                return false;
            }
            return true;
        }

        return false;
    }

    /**
     * 构建微信APP支付入参
     *
     * @param params
     * @return
     */
    protected Map<String, String> buildWechatAppPayParameters(Map<String, String> params) throws PaySettingNotFoundException {
        Map<String, String> result = new HashMap<>();

        PaymentSetting paymentSetting = this.getPaymentSetting(params.get("app_id"));

        result.put("appid", paymentSetting.getPayAppId());
        result.put("mch_id", paymentSetting.getMerchantAccount());
        result.put("nonce_str", RandomUtil.getRandomALLChar(32));
        result.put("body", params.get("desc"));
        result.put("out_trade_no", params.get("order_no"));
        result.put("total_fee", params.get("pay_amount"));
        result.put("spbill_create_ip", LOCALHOST_IP);
        result.put("notify_url", this.getConfig(Config.PAY_WECHAT_NOTIFY_URL));
        result.put("trade_type", "APP");
        result.put("api_secret", paymentSetting.getPayKey());
        result.put("attach", buildPayAttachData(params));

        return result;
    }

    /**
     * 验证微信原生扫码支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkWechatNativePayParameters(Map<String, String> params) {
        if (!CollectionUtils.isEmpty(params)) {
            if (StringUtils.isEmpty(params.get("app_id")) ||
                    StringUtils.isEmpty(params.get("order_no")) ||
                    StringUtils.isEmpty(params.get("pay_amount")) ||
                    StringUtils.isEmpty(params.get("desc")) ||
                    StringUtils.isEmpty(params.get("topic"))) {
                return false;
            }
            return true;
        }

        return false;
    }

    /**
     * 验证微信刷卡支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkWechatMicroPayParameters(Map<String, String> params) {
        return CollectionUtils.isEmpty(params)
                || StringUtils.isEmpty(params.get("app_id"))
                || StringUtils.isEmpty(params.get("order_no"))
                || StringUtils.isEmpty(params.get("pay_amount"))
                || StringUtils.isEmpty(params.get("desc"))
                || StringUtils.isEmpty(params.get("auth_code"));
    }

    /**
     * 构建微信刷卡支付入参
     *
     * @param params
     * @return
     */
    protected WechatMicroPayRequestDTO buildWechatMicroPayParameters(Map<String, String> params) throws PaySettingNotFoundException {
        WechatMicroPayRequestDTO wechatMicroPayRequestDTO = new WechatMicroPayRequestDTO();

        PaymentSetting paymentSetting = this.getPaymentSetting(params.get("app_id"));

        wechatMicroPayRequestDTO.setAppid(params.get("app_id"));
        wechatMicroPayRequestDTO.setMchId(paymentSetting.getMerchantAccount());
        wechatMicroPayRequestDTO.setNonceStr(RandomUtil.getRandomALLChar(32));
        wechatMicroPayRequestDTO.setBody(params.get("desc"));
        wechatMicroPayRequestDTO.setOutTradeNo(params.get("order_no"));
        wechatMicroPayRequestDTO.setTotalFee(params.get("pay_amount"));
        wechatMicroPayRequestDTO.setSpbillCreateIp(LOCALHOST_IP);
        wechatMicroPayRequestDTO.setAuthCode(params.get("auth_code"));
        wechatMicroPayRequestDTO.setApiSecret(paymentSetting.getPayKey());

        return wechatMicroPayRequestDTO;
    }

    /**
     * 构建微信订单查询或撤销入参
     *
     * @param params
     * @return
     */
    protected String buildQueryOrReverseWechatOrderParameters(Map<String, String> params, PayCode payCode) {
        Map<String, String> result = new HashMap<>();
        switch (payCode) {
            case WECHAT:
                result.put("pay_code", "WECHAT");
                break;
            case ALIPAY:
                result.put("pay_code", "ALIPAY");
                break;
            default:
                break;
        }
        result.put("app_id", params.get("app_id"));
        result.put("order_no", params.get("order_no"));

        return JsonUtils.toJsonString(result);
    }

    /**
     * 构建微信原生扫码支付入参
     *
     * @param params
     * @return
     */
    protected Map<String, String> buildWechatNativePayParameters(Map<String, String> params) throws PaySettingNotFoundException {
        Map<String, String> result = new HashMap<>();

        PaymentSetting paymentSetting = this.getPaymentSetting(params.get("app_id"));

        result.put("appid", paymentSetting.getPayAppId());
        result.put("mch_id", paymentSetting.getMerchantAccount());
        result.put("nonce_str", RandomUtil.getRandomALLChar(32));
        result.put("body", params.get("desc"));
        result.put("out_trade_no", params.get("order_no"));
        result.put("total_fee", params.get("pay_amount"));
        result.put("spbill_create_ip", LOCALHOST_IP);
        result.put("notify_url", this.getConfig(Config.PAY_WECHAT_NOTIFY_URL));
        result.put("trade_type", "NATIVE");
        result.put("api_secret", paymentSetting.getPayKey());
        result.put("attach", buildPayAttachData(params));

        return result;
    }

    /**
     * 验证微信H5支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkWechatWebPayParameters(Map<String, String> params) {
        return StringUtils.isEmpty(params.get("app_id")) ||
                StringUtils.isEmpty(params.get("desc")) ||
                StringUtils.isEmpty(params.get("order_no")) ||
                StringUtils.isEmpty(params.get("pay_amount"));
    }

    /**
     * 构建微信H5支付入参
     *
     * @param params
     * @return
     * @throws PaySettingNotFoundException
     */
    protected WechatWebPayRequestDTO buildWechatWebPayParameters(Map<String, String> params) throws PaySettingNotFoundException {
        String appId = params.get("app_id");
        PaymentSetting paymentSetting = this.getPaymentSetting(appId);

        WechatWebPayRequestDTO payRequestDTO = new WechatWebPayRequestDTO();
        payRequestDTO.setAppid(appId);
        payRequestDTO.setMchId(paymentSetting.getMerchantAccount());
        payRequestDTO.setNonceStr(RandomUtil.getRandomALLChar(32));
        payRequestDTO.setBody(params.get("desc"));
        payRequestDTO.setOutTradeNo(params.get("order_no"));
        payRequestDTO.setTotalFee(params.get("pay_amount"));
        payRequestDTO.setSpbillCreateIp(StringUtils.isEmpty(params.get("client_ip")) ? LOCALHOST_IP : params.get("client_ip"));
        payRequestDTO.setNotifyUrl(getConfig(Config.PAY_WECHAT_NOTIFY_URL));
        payRequestDTO.setTradeType("MWEB");
        payRequestDTO.setSceneInfo("{\"h5_info\": {\"type\":\"Wap\",\"wap_url\": \"https://pay.yunyichina.cn\",\"wap_name\": \"云医支付\"}}");

        payRequestDTO.setAttach(buildPayAttachData(params));

        payRequestDTO.setSign(SignUtil.wechatPaySign(Jackson.fromObject(payRequestDTO, Map.class), paymentSetting.getPayKey()));

        return payRequestDTO;
    }

    /**
     * 验证微信小程序支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkWechatAppletPayParameters(Map<String, String> params) {
        if (!CollectionUtils.isEmpty(params)) {
            if (StringUtils.isEmpty(params.get("app_id")) ||
                    StringUtils.isEmpty(params.get("order_no")) ||
                    StringUtils.isEmpty(params.get("pay_amount")) ||
                    StringUtils.isEmpty(params.get("desc")) ||
                    StringUtils.isEmpty(params.get("openid"))) {
                return false;
            }
            return true;
        }

        return false;
    }

    /**
     * 构建支付商家数据包
     *
     * @return
     */
    private String buildPayAttachData(Map<String, String> params) {
        Map<String, String> result = new HashMap<>();

        String appId = params.get("app_id");
        if (!StringUtils.isEmpty(appId)) {
            result.put("app_id", appId);
        }

        String payCode = params.get("pay_code");
        if (!StringUtils.isEmpty(payCode)) {
            result.put("pay_code", payCode);
            result.put("pay_mode", String.valueOf(getPayModeByPayCode(payCode)));
        }

        String topic = params.get("topic");
        if (!StringUtils.isEmpty(topic)) {
            result.put("topic", topic);
        }

        String isInsurance = params.get("is_insurance");
        if (!StringUtils.isEmpty(isInsurance)) {
            result.put("is_insurance", isInsurance);
        }

        return JsonUtils.toJsonString(result);
    }

    /**
     * 验证支付宝扫码付参数
     *
     * @param params
     * @return
     */
    protected boolean checkAlipayPrecreatePayParameters(Map<String, String> params) {
        if (!CollectionUtils.isEmpty(params)) {
            if (StringUtils.isEmpty(params.get("app_id")) ||
                    StringUtils.isEmpty(params.get("order_no")) ||
                    StringUtils.isEmpty(params.get("pay_amount")) ||
                    StringUtils.isEmpty(params.get("desc")) ||
                    StringUtils.isEmpty(params.get("topic"))) {

                return false;
            }

            return true;
        }

        return false;
    }

    /**
     * 构建支付宝扫码付入参
     *
     * @param params
     * @return
     */
    protected Map<String, String> buildAlipayPrecreatePayParameters(Map<String, String> params) throws PaySettingNotFoundException {
        Map<String, String> result = new HashMap<>();

        String appid = params.get("app_id");

        PaymentSetting paymentSetting = this.getPaymentSetting(appid);
        String signType = "RSA";
        if (paymentSetting.getPayPublicKey().length() > 256) {
            signType = "RSA2";
        }

        result.put("private_key", paymentSetting.getPrivateKey());
        result.put("public_key", paymentSetting.getPublicKey());
        result.put("pay_public_key", paymentSetting.getPayPublicKey());

        result.put("app_id", appid);
        result.put("method", "alipay.trade.precreate");
        result.put("format", "JSON");
        result.put("charset", "utf-8");
        result.put("sign_type", signType);
        result.put("timestamp", DateUtils.dateToString(Calendar.getInstance().getTime()));
        result.put("version", "1.0");
        result.put("notify_url", this.buildAlipayNotifyUrl(params));

        Map<String, Object> businessParams = new HashMap<>();
        businessParams.put("out_trade_no", params.get("order_no"));
        businessParams.put("total_amount",
                new BigDecimal(params.get("pay_amount")).divide(
                        new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        businessParams.put("subject", params.get("desc"));
        businessParams.put("timeout_express", "5m");

        Map<String, String> extendParams = new HashMap<>();
        extendParams.put(Alipay.SYS_SERVICE_PROVIDER_ID, getConfig(Config.ALIPAY_SYS_SERVICE_PROVIDER_ID));
        businessParams.put("extend_params", extendParams);

        result.put("biz_content", JsonUtils.toJsonString(businessParams));

        return result;
    }

    /**
     * 验证支付宝手机网站支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkAlipayWapPayParameters(Map<String, String> params) {
        return CollectionUtils.isEmpty(params) ||
                StringUtils.isEmpty(params.get("app_id")) ||
                StringUtils.isEmpty(params.get("order_no")) ||
                StringUtils.isEmpty(params.get("pay_amount")) ||
                StringUtils.isEmpty(params.get("desc")) ||
                StringUtils.isEmpty(params.get("topic"));
    }

    /**
     * 构建支付宝手机网站支付入参
     *
     * @param params
     * @return
     */
    protected Map<String, String> buildAlipayWapPayParameters(Map<String, String> params) throws PaySettingNotFoundException {
        Map<String, String> result = new HashMap<>();

        String appid = params.get("app_id");

        PaymentSetting paymentSetting = this.getPaymentSetting(appid);
        String signType = "RSA";
        if (paymentSetting.getPayPublicKey().length() > 256) {
            signType = "RSA2";
        }

        result.put("private_key", paymentSetting.getPrivateKey());
        result.put("public_key", paymentSetting.getPublicKey());
        result.put("pay_public_key", paymentSetting.getPayPublicKey());

        result.put("app_id", appid);
        result.put("method", "alipay.trade.wap.pay");
        result.put("format", "JSON");
        String returnUrl = params.get("return_url");
        if (!StringUtils.isEmpty(returnUrl)) {
            result.put("return_url", returnUrl);
        }
        result.put("charset", "utf-8");
        result.put("sign_type", signType);
        result.put("timestamp", DateUtils.dateToString(Calendar.getInstance().getTime()));
        result.put("version", "1.0");
        result.put("notify_url", this.buildAlipayNotifyUrl(params));

        Map<String, Object> businessParams = new HashMap<>();
        businessParams.put("subject", params.get("desc"));
        businessParams.put("out_trade_no", params.get("order_no"));
        businessParams.put("total_amount",
                new BigDecimal(params.get("pay_amount")).divide(
                        new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        businessParams.put("timeout_express", "24h");
        businessParams.put("product_code", "QUICK_WAP_PAY");

        Map<String, String> extendParams = new HashMap<>();
        extendParams.put(Alipay.SYS_SERVICE_PROVIDER_ID, getConfig(Config.ALIPAY_SYS_SERVICE_PROVIDER_ID));
        businessParams.put("extend_params", extendParams);

        result.put("biz_content", JsonUtils.toJsonString(businessParams));

        return result;
    }

    /**
     * 验证支付宝APP支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkAlipayAppPayParameters(Map<String, String> params) {
        if (!CollectionUtils.isEmpty(params)) {
            if (StringUtils.isEmpty(params.get("app_id")) ||
                    StringUtils.isEmpty(params.get("order_no")) ||
                    StringUtils.isEmpty(params.get("pay_amount")) ||
                    StringUtils.isEmpty(params.get("desc")) ||
                    StringUtils.isEmpty(params.get("topic"))) {
                return false;
            }
            return true;
        }

        return false;
    }

    /**
     * 构建支付宝APP支付入参
     *
     * @param params
     * @return
     */
    protected Map<String, String> buildAlipayAppPayParameters(Map<String, String> params) throws PaySettingNotFoundException {
        Map<String, String> result = new HashMap<>();

        String appid = params.get("app_id");
        PaymentSetting paymentSetting = this.getPaymentSetting(appid);
        String signType = "RSA";
        if (paymentSetting.getPayPublicKey().length() > 256) {
            signType = "RSA2";
        }

        result.put("private_key", paymentSetting.getPrivateKey());

        result.put("app_id", appid);
        result.put("method", "alipay.trade.app.pay");
        result.put("charset", "utf-8");
        result.put("sign_type", signType);
        result.put("timestamp", DateUtils.dateToString(Calendar.getInstance().getTime()));
        result.put("version", "1.0");
        result.put("notify_url", getConfig(Config.PAY_ALIPAY_NOTIFY_URL));

        Map<String, Object> businessParams = new HashMap<>();
        businessParams.put("subject", params.get("desc"));
        businessParams.put("out_trade_no", params.get("order_no"));
        businessParams.put("timeout_express", "24h");
        businessParams.put("total_amount",
                new BigDecimal(params.get("pay_amount")).divide(
                        new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        businessParams.put("product_code", "QUICK_MSECURITY_PAY");

        HashMap<String, String> passbackParams = new HashMap<>();
        passbackParams.put("pay_code", params.get("pay_code"));
        passbackParams.put("pay_mode", String.valueOf(getPayModeByPayCode(params.get("pay_code"))));
        passbackParams.put("topic", params.get("topic"));
        businessParams.put("passback_params", passbackParams);

        Map<String, String> extendParams = new HashMap<>();
        extendParams.put(Alipay.SYS_SERVICE_PROVIDER_ID, getConfig(Config.ALIPAY_SYS_SERVICE_PROVIDER_ID));
        businessParams.put("extend_params", extendParams);

        result.put("biz_content", JsonUtils.toJsonString(businessParams));
        return result;
    }

    /**
     * 验证支付宝当面付(条码支付/声波支付)支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkAlipayFacePayParameters(Map<String, String> params) {
        return CollectionUtils.isEmpty(params) ||
                StringUtils.isEmpty(params.get("app_id")) ||
                (StringUtils.isEmpty(params.get("order_no")) &&
                        StringUtils.isEmpty(params.get("pay_order_no"))) ||
                StringUtils.isEmpty(params.get("desc")) ||
                StringUtils.isEmpty(params.get("pay_amount")) ||
                StringUtils.isEmpty(params.get("auth_code"));
    }

    /**
     * 构建支付宝条码付支付入参
     *
     * @param params
     * @return
     */
    protected AlipayBarPayRequestDTO buildAlipayBarPayRequestDTO(Map<String, String> params) throws PaySettingNotFoundException {
        String appid = params.get("app_id");

        PaymentSetting paymentSetting = this.getPaymentSetting(appid);
        String signType = "RSA";
        if (paymentSetting.getPayPublicKey().length() > 256) {
            signType = "RSA2";
        }

        AlipayBarPayRequestDTO requestDTO = new AlipayBarPayRequestDTO();
        requestDTO.setAppId(appid);
        requestDTO.setPayPublicKey(paymentSetting.getPayPublicKey());
        requestDTO.setPrivateKey(paymentSetting.getPrivateKey());
        requestDTO.setFormat("JSON");
        requestDTO.setCharset("utf-8");
        requestDTO.setSignType(signType);

        Map<String, Object> bizParams = new HashMap<>();
        bizParams.put("out_trade_no", params.get("order_no"));
        bizParams.put("scene", "wave_code");
        bizParams.put("auth_code", params.get("auth_code"));
        bizParams.put("subject", params.get("desc"));
        bizParams.put("total_amount",
                new BigDecimal(params.get("pay_amount")).divide(
                        new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP).toString());

        Map<String, String> extendParams = new HashMap<>();
        extendParams.put(Alipay.SYS_SERVICE_PROVIDER_ID, getConfig(Config.ALIPAY_SYS_SERVICE_PROVIDER_ID));
        bizParams.put("extend_params", extendParams);

        requestDTO.setBizContent(Jackson.toJson(bizParams));

        return requestDTO;
    }

    /**
     * 构建支付宝当面付(条码支付/声波支付)入参
     *
     * @param params
     * @return
     */
    protected Map<String, String> buildAlipayFacePayParameters(Map<String, String> params) throws PaySettingNotFoundException {
        Map<String, String> result = new HashMap<>();

        String appid = params.get("app_id");

        PaymentSetting paymentSetting = this.getPaymentSetting(appid);
        String signType = "RSA";
        if (paymentSetting.getPayPublicKey().length() > 256) {
            signType = "RSA2";
        }

        result.put("private_key", paymentSetting.getPrivateKey());
        result.put("public_key", paymentSetting.getPublicKey());
        result.put("pay_public_key", paymentSetting.getPayPublicKey());

        result.put("app_id", appid);
        result.put("method", "alipay.trade.pay");
        result.put("format", "JSON");
        result.put("charset", "utf-8");
        result.put("sign_type", signType);
        result.put("timestamp", DateUtils.dateToString(Calendar.getInstance().getTime()));
        result.put("version", "1.0");

        Map<String, Object> businessParams = new HashMap<>();
        businessParams.put("out_trade_no", params.get("order_no"));
        switch (PayCode.valueOf(params.get("pay_code"))) {
            case ALIPAYBARPAY:
                businessParams.put("scene", "bar_code");
                break;
            case ALIPAYWAVEPAY:
                businessParams.put("scene", "wave_code");
                break;
            default:
                break;
        }
        businessParams.put("auth_code", params.get("auth_code"));
        businessParams.put("subject", params.get("desc"));
        businessParams.put("total_amount",
                new BigDecimal(params.get("pay_amount")).divide(
                        new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());

        Map<String, String> extendParams = new HashMap<>();
        extendParams.put(Alipay.SYS_SERVICE_PROVIDER_ID, getConfig(Config.ALIPAY_SYS_SERVICE_PROVIDER_ID));
        businessParams.put("extend_params", extendParams);

        result.put("biz_content", JsonUtils.toJsonString(businessParams));

        result.put("pay_mode", String.valueOf(this.getPayModeByPayCode(params.get("pay_code"))));

        return result;
    }

    /**
     * 构建支付宝当面付(条码支付/声波支付)同步通知入参
     *
     * @param inputMap
     * @param requestMap
     * @param responseMap
     * @return
     */
    protected Map<String, String> buildAlipaySyncNotifyParameters(
            Map<String, String> inputMap,
            Map<String, String> requestMap,
            Map<String, String> responseMap) {
        Map<String, String> result = new HashMap<>();

        result.putAll(inputMap);
        result.putAll(requestMap);
        result.putAll(responseMap);

        result.put("trade_status", String.valueOf(AlipayNotify.TradeStatus.TRADE_SUCCESS));

        return result;
    }

    /**
     * 构建微信刷卡支付同步通知入参
     *
     * @param inputMap
     * @param requestMap
     * @param responseMap
     * @return
     */
    protected Map<String, String> buildWechatPaySyncNotifyParameters(
            Map<String, String> inputMap,
            Map<String, String> requestMap,
            Map<String, String> responseMap) {
        Map<String, String> result = new HashMap<>();

        result.putAll(inputMap);
        result.putAll(requestMap);
        result.putAll(responseMap);

        Map<String, String> attach = new HashMap<>();
        attach.put("pay_code", inputMap.get("pay_code"));

        result.put("attach", buildPayAttachData(attach));

        result.put("return_code", "SUCCESS");
        result.put("result_code", "SUCCESS");

        result.remove("return_code_success");
        result.remove("result_code_success");

        return result;
    }

    /**
     * 验证支付宝医保支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkAlipayMedicarePayParameters(Map<String, String> params) {
        if (!CollectionUtils.isEmpty(params)) {
            if (StringUtils.isEmpty(params.get("app_id")) ||
                    StringUtils.isEmpty(params.get("order_no")) ||
                    StringUtils.isEmpty(params.get("pay_amount")) ||
                    StringUtils.isEmpty(params.get("desc")) ||
                    StringUtils.isEmpty(params.get("topic")) ||
                    StringUtils.isEmpty(params.get("openid")) ||

                    StringUtils.isEmpty(params.get("return_url")) ||
                    StringUtils.isEmpty(params.get("scene")) ||
                    StringUtils.isEmpty(params.get("industry")) ||
                    StringUtils.isEmpty(params.get("org_code")) ||
                    StringUtils.isEmpty(params.get("org_name")) ||
                    StringUtils.isEmpty(params.get("serial_no")) ||
                    StringUtils.isEmpty(params.get("patient_card_type")) ||
                    StringUtils.isEmpty(params.get("patient_card_no")) ||
                    StringUtils.isEmpty(params.get("patient_name"))) {
                return false;
            }

            String scene = params.get("scene");

            switch (Medicare.Scene.valueOf(scene)) {
                case REGISTRATION:
                    if (!StringUtils.isEmpty(params.get("yljgbm")) &&
                            !StringUtils.isEmpty(params.get("ylzh")) &&
                            !StringUtils.isEmpty(params.get("czybm")) &&
                            !StringUtils.isEmpty(params.get("czyxm")) &&
                            !StringUtils.isEmpty(params.get("mzlsh")) &&
                            !StringUtils.isEmpty(params.get("ksbm")) &&
                            !StringUtils.isEmpty(params.get("ksmc")) &&
                            !StringUtils.isEmpty(params.get("ghlb")) &&
                            !StringUtils.isEmpty(params.get("ghf")) &&
                            !StringUtils.isEmpty(params.get("zjzj")) &&
                            !StringUtils.isEmpty(params.get("ghhj")) &&
                            !StringUtils.isEmpty(params.get("ssj")) &&
                            !StringUtils.isEmpty(params.get("zsj"))) {
                        return true;
                    }
                    break;
                case TREATMENT:
                    if (!StringUtils.isEmpty(params.get("bill_no"))) {
                        return true;
                    }
                    break;
                default:
                    break;
            }
            return false;
        }

        return false;
    }

    /**
     * 构建支付宝医保支付入参
     *
     * @param params
     * @return
     */
    protected Map<String, String> buildAlipayMedicarePayParameters(Map<String, String> params, PaymentSetting paymentSetting) throws AlipayApiException {
        Map<String, String> baseParams = new HashMap<>();

        String signType = "RSA";
        if (paymentSetting.getPayPublicKey().length() > 256) {
            signType = "RSA2";
        }

        baseParams.put("method", "alipay.commerce.medical.instcard.createandpay");
        baseParams.put("app_id", params.get("app_id"));
        baseParams.put("version", "1.0");
        baseParams.put("timestamp", DateUtils.dateToString(Calendar.getInstance().getTime()));
        baseParams.put("sign_type", signType);
        baseParams.put("return_url", params.get("return_url"));
        baseParams.put("notify_url", this.buildAlipayNotifyUrl(params));
        baseParams.put("auth_token", params.get("auth_token"));
        baseParams.put("sign_flag", "true");

        Map<String, String> businessParams = new HashMap<>();
        businessParams.put("out_trade_no", params.get("order_no"));
        businessParams.put("scene", params.get("scene"));
        businessParams.put("industry", Medicare.Industry.valueOf(params.get("industry")).toString());
        businessParams.put("buyer_id", params.get("openid"));
        businessParams.put("seller_id", paymentSetting.getPartnerId());
        businessParams.put("total_amount",
                new BigDecimal(params.get("pay_amount")).divide(
                        new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        businessParams.put("subject", params.get("desc"));
        businessParams.put("timeout_express", "15m");
        businessParams.put("org_no", this.getOrgNoByOrgCode(params.get("org_code")));
        businessParams.put("org_name", params.get("org_name"));
        businessParams.put("serial_no", params.get("serial_no"));

        String scene = params.get("scene");

        switch (Medicare.Scene.valueOf(scene)) {
            case REGISTRATION:
                businessParams.put("request_content", this.buildMedicarePayRegistrationRequestContent(params));
                break;
            case TREATMENT:
                businessParams.put("bill_no", params.get("bill_no"));
                break;
            default:
                break;
        }

        businessParams.put("gmt_out_create", DateUtils.dateToString(Calendar.getInstance().getTime()));
        businessParams.put("patient_card_type", params.get("patient_card_type"));
        businessParams.put("patient_card_no", params.get("patient_card_no"));
        businessParams.put("patient_name", params.get("patient_name"));
        String patientMobile = params.get("patient_mobile");
        if (!StringUtils.isEmpty(patientMobile)) {
            businessParams.put("patient_mobile", patientMobile);
        }
        businessParams.put("is_insurance", "T");

        Map<String, String> extendParams = new HashMap<>();
        extendParams.put(Alipay.SYS_SERVICE_PROVIDER_ID, getConfig(Config.ALIPAY_SYS_SERVICE_PROVIDER_ID));
        businessParams.put("extend_params", JsonUtils.toJsonString(extendParams));

        Map<String, String> allParams = new HashMap<>();
        allParams.putAll(baseParams);
        allParams.putAll(businessParams);
        allParams.put("sign", this.generateAlipayMedicarePaySign(allParams, paymentSetting.getPrivateKey(), signType));

        return allParams;
    }

    /**
     * 通过HOSPITAL_CODE获取医疗机构编码ORG_CODE
     *
     * @param orgCode
     * @return
     */
    private String getOrgNoByOrgCode(String orgCode) {
        OrgSettingVo orgSettingVo = new OrgSettingVo();
        orgSettingVo.setHospitalCode(orgCode);

        ResCommon<List<OrgSettingVoResponse>> resCommon = cn.yunyichina.provider.framework.dubbo.service.BaseService.getBaseClient().getOrgSettingList(orgSettingVo);
        if (resCommon != null && resCommon.getResult() != null && resCommon.getResult().get(0) != null) {
            List<OrgSettingVoResponse> orgSettingVoResponseList = resCommon.getResult();
            if (!CollectionUtils.isEmpty(orgSettingVoResponseList)) {
                OrgSettingVoResponse orgSettingVoResponse = orgSettingVoResponseList.get(0);
                return orgSettingVoResponse.getMedicalHisCode();
            }
        }

        throw new RuntimeException("Cannot find org_no from BaseService");
    }

    /**
     * 构造支付宝通知url
     *
     * @param params
     * @return
     */
    private String buildAlipayNotifyUrl(Map<String, String> params) {
        StringBuilder builder = new StringBuilder();

        builder.append(this.getConfig(Config.PAY_ALIPAY_NOTIFY_URL)).
                append("?pay_code=").append(params.get("pay_code")).
                append("&pay_mode=").append(this.getPayModeByPayCode(params.get("pay_code"))).
                append("&topic=").append(params.get("topic"));

        return builder.toString();
    }

    /**
     * 构建医保挂号黑盒结构体
     *
     * @param params
     * @return
     */
    private String buildMedicarePayRegistrationRequestContent(Map<String, String> params) {
        RegistrationRequestContent.RegistrationOrderPay registrationOrderPay = JsonUtils.parseObject(params, RegistrationRequestContent.RegistrationOrderPay.class);

        RegistrationRequestContent.RegistrationOrderPay.MZGHDJ mzghdj = JsonUtils.parseObject(params, RegistrationRequestContent.RegistrationOrderPay.MZGHDJ.class);
        mzghdj.setGhf(new BigDecimal(mzghdj.getGhf()).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        mzghdj.setZjzj(new BigDecimal(mzghdj.getZjzj()).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        mzghdj.setGhhj(new BigDecimal(mzghdj.getGhhj()).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        mzghdj.setSsj(new BigDecimal(mzghdj.getSsj()).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        mzghdj.setZsj(new BigDecimal(mzghdj.getZsj()).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());

        String ksmc = mzghdj.getKsmc();
        if (!StringUtils.isEmpty(ksmc) && ksmc.getBytes(Charset.forName(Encoding.UTF8)).length > 18) {
            mzghdj.setKsmc(cn.yunyichina.utils.commons.StringUtils.substrWithByteLengthAndCharset(ksmc, 18, Charset.forName("UTF-8")));
        }

        registrationOrderPay.setMzghdj(mzghdj);

        RegistrationRequestContent registrationRequestContent = new RegistrationRequestContent();

        registrationRequestContent.setRegistrationOrderPay(registrationOrderPay);

        return JsonUtils.toJsonString(registrationRequestContent);
    }

    /**
     * 构建支付宝医保支付挂号黑盒结构体扩展参数
     *
     * @param params
     * @return
     */
    private String buildAlipayMedicarePayRegistrationRequestContentExt(Map<String, String> params) {
        RegistrationRequestContentExt registrationRequestContentExt = JsonUtils.parseObject(params, RegistrationRequestContentExt.class);
        registrationRequestContentExt.setGmtOutCreate(DateUtils.getDatetime());
        return JsonUtils.toJsonString(registrationRequestContentExt);
    }

    /**
     * 生成支付宝医保支付签名
     *
     * @param params
     * @return
     */
    private String generateAlipayMedicarePaySign(Map<String, String> params, String privateKey, String signType) throws AlipayApiException {
        List<String> signList = new ArrayList<>();

        for (Map.Entry<String, String> entry : params.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (!StringUtils.isEmpty(value)) {
                String param = key.concat("=").concat(value);
                signList.add(param);
            }
        }

        String[] signArray = new String[signList.size()];
        signList.toArray(signArray);
        Arrays.sort(signArray);

        StringBuilder buffer = new StringBuilder();
        for (String str : signArray) {
            buffer.append(str).append("&");
        }

        String content = buffer.deleteCharAt(buffer.length() - 1).toString();

        if ("RSA".equals(signType)) {
            return AlipaySignature.rsaSign(content, privateKey, Encoding.GBK);
        }

        return AlipaySignature.rsa256Sign(content, privateKey, Encoding.GBK);
    }

    /**
     * 拼装支付宝医保支付url
     *
     * @param params
     * @return
     */
    protected String assembleAlipayMedicarePayUrl(Map<String, String> params) throws UnsupportedEncodingException {
        String sign = URLEncoder.encode(params.remove("sign"), Encoding.GBK);

        List<String> paramList = new ArrayList<>();

        for (Map.Entry<String, String> entry : params.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (!StringUtils.isEmpty(value)) {
                String encodedValue = URLEncoder.encode(value, Encoding.GBK);
                String param = key.concat("=").concat(encodedValue);
                paramList.add(param);
            }
        }

        String[] paramArray = new String[paramList.size()];
        paramList.toArray(paramArray);
        Arrays.sort(paramArray);

        StringBuilder urlBuilder = new StringBuilder();

        String isSandbox = getConfig(Config.PAY_ALIPAY_SANDBOX_FLAG);
        if (!StringUtils.isEmpty(isSandbox) && Boolean.valueOf(isSandbox)) {
            urlBuilder.append("http://openapi.alipaydev.com/gateway.do?");
        } else {
            urlBuilder.append("https://openapi.alipay.com/gateway.do?");
        }

        for (String str : paramArray) {
            urlBuilder.append(str).append("&");
        }

        return urlBuilder.toString().concat("sign=").concat(sign);
    }

    /**
     * 验证支付宝医保当面付参数
     *
     * @param params
     * @return
     */
    protected boolean checkAlipayMedicareFacePayParameters(Map<String, String> params) {
        if (!CollectionUtils.isEmpty(params)) {
            if (StringUtils.isEmpty(params.get("app_id")) ||
                    StringUtils.isEmpty(params.get("order_no")) ||
                    StringUtils.isEmpty(params.get("desc")) ||
                    StringUtils.isEmpty(params.get("total_amount")) ||

                    StringUtils.isEmpty(params.get("auth_code")) ||

                    StringUtils.isEmpty(params.get("treatment")) ||

                    StringUtils.isEmpty(params.get("name")) ||
                    StringUtils.isEmpty(params.get("mobile")) ||
                    StringUtils.isEmpty(params.get("cert_type")) ||
                    StringUtils.isEmpty(params.get("cert_id")) ||
                    StringUtils.isEmpty(params.get("serial_no"))) {
                return false;
            }

            String treatment = params.get("treatment");

            if (Medicare.Scene.valueOf(treatment).equals(Medicare.Scene.REGISTRATION)) {
                return !StringUtils.isEmpty(params.get("yljgbm")) &&
                        !StringUtils.isEmpty(params.get("ylzh")) &&
                        !StringUtils.isEmpty(params.get("czybm")) &&
                        !StringUtils.isEmpty(params.get("czyxm")) &&
                        !StringUtils.isEmpty(params.get("mzlsh")) &&
                        !StringUtils.isEmpty(params.get("ksbm")) &&
                        !StringUtils.isEmpty(params.get("ksmc")) &&
                        !StringUtils.isEmpty(params.get("ghlb")) &&
                        !StringUtils.isEmpty(params.get("ghf")) &&
                        !StringUtils.isEmpty(params.get("zjzj")) &&
                        !StringUtils.isEmpty(params.get("ghhj")) &&
                        !StringUtils.isEmpty(params.get("ssj")) &&
                        !StringUtils.isEmpty(params.get("zsj")) &&

                        !StringUtils.isEmpty(params.get("org_no")) &&
                        !StringUtils.isEmpty(params.get("out_trade_no"));
            } else {
                return Medicare.Scene.valueOf(treatment).equals(Medicare.Scene.TREATMENT) && !StringUtils.isEmpty(params.get("bill_no"));
            }
        }

        return false;
    }

    /**
     * 构建支付宝医保当面付入参
     *
     * @param params
     * @return
     */
    protected Map<String, String> buildAlipayMedicareFacePayParameters(Map<String, String> params) throws PaySettingNotFoundException {
        Map<String, String> result = new HashMap<>();

        String appid = params.get("app_id");

        PaymentSetting paymentSetting = this.getPaymentSetting(appid);
        String signType = "RSA";
        if (paymentSetting.getPayPublicKey().length() > 256) {
            signType = "RSA2";
        }

        result.put("private_key", paymentSetting.getPrivateKey());
        result.put("public_key", paymentSetting.getPublicKey());
        result.put("pay_public_key", paymentSetting.getPayPublicKey());

        result.put("app_id", appid);
        result.put("method", "alipay.trade.pay");
        result.put("format", "JSON");
        result.put("charset", "utf-8");
        result.put("sign_type", signType);
        result.put("timestamp", DateUtils.dateToString(Calendar.getInstance().getTime()));
        result.put("version", "1.0");

        Map<String, Object> businessParams = new HashMap<>();
        businessParams.put("out_trade_no", params.get("order_no"));
        switch (PayCode.valueOf(params.get("pay_code"))) {
            case ALIPAYMEDICAREBARPAY:
                businessParams.put("scene", "bar_code");
                break;
            case ALIPAYMEDICAREWAVEPAY:
                businessParams.put("scene", "wave_code");
                break;
            default:
                break;
        }
        businessParams.put("auth_code", params.get("auth_code"));
        businessParams.put("subject", params.get("desc"));
        businessParams.put("total_amount",
                new BigDecimal(params.get("total_amount")).divide(
                        new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());

        Map<String, String> extendParams = new HashMap<>();
        extendParams.put("IS_INSURANCE_PAY", "T");
        extendParams.put("medical_card_inst_id", "SZHRSS");
        extendParams.put("industry", "HOSPITAL");

        extendParams.put(Alipay.SYS_SERVICE_PROVIDER_ID, getConfig(Config.ALIPAY_SYS_SERVICE_PROVIDER_ID));

        String treatment = params.get("treatment");
        if (Medicare.Scene.valueOf(treatment).equals(Medicare.Scene.REGISTRATION)) {
            extendParams.put("medical_request_content", buildMedicarePayRegistrationRequestContent(params));
            extendParams.put("medical_request_ext", buildAlipayMedicarePayRegistrationRequestContentExt(params));
        } else if (Medicare.Scene.valueOf(treatment).equals(Medicare.Scene.TREATMENT)) {
            RegistrationRequestContentExt registrationRequestContentExt = new RegistrationRequestContentExt();
            registrationRequestContentExt.setSerialNo(params.get("serial_no"));
            registrationRequestContentExt.setBillNo(params.get("bill_no"));
            registrationRequestContentExt.setOrgNo(getOrgNoByOrgCode(params.get("hospital_code")));
            registrationRequestContentExt.setOutTradeNo(params.get("order_no"));
            registrationRequestContentExt.setGmtOutCreate(DateUtils.getDatetime());
            extendParams.put("medical_request_ext", JsonUtils.toJsonString(registrationRequestContentExt));
        }

        businessParams.put("extend_params", extendParams);

        Map<String, String> extUserInfo = new HashMap<>();
        extUserInfo.put("name", params.get("name"));
        extUserInfo.put("mobile", params.get("mobile"));
        extUserInfo.put("cert_type", params.get("cert_type"));
        extUserInfo.put("cert_id", params.get("cert_id"));

        businessParams.put("ext_user_info", extUserInfo);

        result.put("biz_content", JSON.toJSONString(businessParams));

        result.put("pay_mode", String.valueOf(this.getPayModeByPayCode(params.get("pay_code"))));

        return result;
    }

    /**
     * 验证微信医疗支付基本参数
     *
     * @param params
     * @return
     */
    protected boolean checkWechatMedicalPayBaseParameters(Map<String, String> params) {
        if (StringUtils.isEmpty(params.get("order_type")) ||
                StringUtils.isEmpty(params.get("app_id")) ||
                StringUtils.isEmpty(params.get("openid")) ||
                StringUtils.isEmpty(params.get("order_no")) ||
                StringUtils.isEmpty(params.get("is_insurance")) ||
                StringUtils.isEmpty(params.get("hospital_code")) ||
                StringUtils.isEmpty(params.get("hospital_name")) ||
                StringUtils.isEmpty(params.get("total_amount")) ||
                StringUtils.isEmpty(params.get("cash_amount")) ||
                StringUtils.isEmpty(params.get("title")) ||
                StringUtils.isEmpty(params.get("return_url"))) {
            return false;
        }

        return true;
    }

    /**
     * 构建微信医疗支付自费入参
     *
     * @param params
     * @return
     */
    protected Map<String, String> buildWechatMedicalSelfPayParameters(Map<String, String> params) {
        Map<String, String> result = new HashMap<>();
        result.put("api_secret", params.get("api_secret"));
        result.put("access_token", params.get("access_token"));

        String orderType = params.get("order_type");
        String orderTypeValue = Medicare.WechatOrderType.valueOf(orderType).getValue();

        result.put("order_type", orderTypeValue);
        result.put("appid", params.get("appid"));
        result.put("mch_id", params.get("mch_id"));
        result.put("sub_appid", params.get("sub_appid"));
        result.put("sub_mch_id", params.get("sub_mch_id"));
        result.put("sub_openid", params.get("openid"));
        result.put("hosp_out_trade_no", params.get("order_no"));
        result.put("hospital_name", params.get("hospital_name"));
        result.put("nonce_str", RandomUtil.getRandomALLChar(32));
        result.put("total_fee", params.get("total_amount"));
        result.put("cash_fee", params.get("cash_amount"));
        result.put("allow_fee_change", "0");
        result.put("spbill_create_ip", LOCALHOST_IP);
        result.put("notify_url", this.getConfig(Config.PAY_WECHAT_MEDICAL_NOTIFY_URL));
        result.put("body", params.get("title"));
        result.put("return_url", params.get("return_url"));
        result.put("pay_type", "2");

        if (Medicare.WechatOrderType.DIAGPAY.getValue().equals(orderTypeValue)) {
            result.put("request_content", this.buildWechatMedicalDiagMedicarePayPrescriptionRequestContent(params));
        }

        return result;
    }

    /**
     * 验证微信门诊医保支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkWechatMedicalMedicarePayParameters(Map<String, String> params) {
        if (StringUtils.isEmpty(params.get("insurance_amount")) ||
                StringUtils.isEmpty(params.get("card_type")) ||
                StringUtils.isEmpty(params.get("card_no")) ||
                StringUtils.isEmpty(params.get("user_name")) ||
                StringUtils.isEmpty(params.get("serial_no"))) {
            return false;
        }

        String orderType = params.get("order_type");

        switch (Medicare.WechatOrderType.valueOf(orderType)) {
            case REGPAY:
                if (StringUtils.isEmpty(params.get("yljgbm")) &&
                        StringUtils.isEmpty(params.get("ylzh")) &&
                        StringUtils.isEmpty(params.get("czybm")) &&
                        StringUtils.isEmpty(params.get("czyxm")) &&
                        StringUtils.isEmpty(params.get("mzlsh")) &&
                        StringUtils.isEmpty(params.get("ksbm")) &&
                        StringUtils.isEmpty(params.get("ksmc")) &&
                        StringUtils.isEmpty(params.get("ghlb")) &&
                        StringUtils.isEmpty(params.get("ghf")) &&
                        StringUtils.isEmpty(params.get("zjzj")) &&
                        StringUtils.isEmpty(params.get("ghhj")) &&
                        StringUtils.isEmpty(params.get("ssj")) &&
                        StringUtils.isEmpty(params.get("zsj"))) {
                    return false;
                }
                return true;
            case DIAGPAY:
                if (StringUtils.isEmpty(params.get("bill_no"))) {
                    return false;
                }
                return true;
            default:
                return false;
        }
    }

    /**
     * 构建微信医保支付入参
     *
     * @param params
     * @return
     */
    protected Map<String, String> buildWechatMedicalMedicarePayParameters(Map<String, String> params) {
        Map<String, String> result = new HashMap<>();
        result.put("api_secret", params.get("api_secret"));
        result.put("access_token", params.get("access_token"));

        String orderType = params.get("order_type");

        result.put("order_type", Medicare.WechatOrderType.valueOf(orderType).getValue());
        result.put("appid", params.get("appid"));
        result.put("mch_id", params.get("mch_id"));
        result.put("sub_appid", params.get("sub_appid"));
        result.put("sub_mch_id", params.get("sub_mch_id"));
        result.put("sub_openid", params.get("openid"));
        result.put("hosp_out_trade_no", params.get("order_no"));
        result.put("hospital_name", params.get("hospital_name"));
        result.put("nonce_str", RandomUtil.getRandomALLChar(32));
        result.put("total_fee", params.get("total_amount"));
        result.put("cash_fee", params.get("cash_amount"));
        result.put("allow_fee_change", "0");
        result.put("spbill_create_ip", LOCALHOST_IP);
        result.put("notify_url", this.getConfig(Config.PAY_WECHAT_MEDICAL_NOTIFY_URL));
        result.put("body", params.get("title"));
        result.put("return_url", params.get("return_url"));
        result.put("pay_type", Integer.valueOf(params.get("cash_amount")) == 0 ? "2" : "3");

        result.put("insurance_fee", params.get("insurance_amount"));
        result.put("user_card_type", params.get("card_type"));
        result.put("user_card_no", DigestUtils.md5Hex(params.get("card_no")));
        result.put("user_name", params.get("user_name"));
        result.put("serial_no", params.get("serial_no"));

        result.put("org_no", this.getOrgNoByOrgCode(params.get("hospital_code")));
        result.put("gmt_out_create", DateUtils.getDatetime(DateUtils.YYYYMMDDHHMMSS_NOBAR));

        switch (Medicare.WechatOrderType.valueOf(orderType)) {
            case REGPAY:
                result.put("request_content", this.buildWechatMedicalRegMedicarePayRegistrationRequestContent(params));
                break;
            case DIAGPAY:
                result.put("bill_no", params.get("bill_no"));
                result.put("request_content", this.buildWechatMedicalDiagMedicarePayPrescriptionRequestContent(params));
                break;
            default:
                break;
        }

        return result;
    }

    /**
     * 构建微信医保支付挂号黑盒结构体
     *
     * @param params
     * @return
     */
    private String buildWechatMedicalRegMedicarePayRegistrationRequestContent(Map<String, String> params) {
        RegistrationRequestContent.RegistrationOrderPay registrationOrderPay = JsonUtils.parseObject(params, RegistrationRequestContent.RegistrationOrderPay.class);

        RegistrationRequestContent.RegistrationOrderPay.MZGHDJ mzghdj = JsonUtils.parseObject(params, RegistrationRequestContent.RegistrationOrderPay.MZGHDJ.class);
        mzghdj.setGhf(mzghdj.getGhf());
        mzghdj.setZjzj(mzghdj.getZjzj());
        mzghdj.setGhhj(mzghdj.getGhhj());
        mzghdj.setSsj(mzghdj.getSsj());
        mzghdj.setZsj(mzghdj.getZsj());

        registrationOrderPay.setMzghdj(mzghdj);

        RegistrationRequestContent registrationRequestContent = new RegistrationRequestContent();

        registrationRequestContent.setRegistrationOrderPay(registrationOrderPay);

        return JsonUtils.toJsonString(registrationRequestContent);
    }

    /**
     * 构建微信医保支付门诊黑盒结构体
     *
     * @param params
     * @return
     */
    private String buildWechatMedicalDiagMedicarePayPrescriptionRequestContent(Map<String, String> params) {
        String prescriptionDetail = params.get("prescription_detail");
        List<PrescriptionRequestContent.PrescriptionDetail> prescriptionDetailList = null;

        if (!StringUtils.isEmpty(prescriptionDetail)) {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);

            try {
                prescriptionDetailList = objectMapper.readValue(prescriptionDetail,
                        objectMapper.getTypeFactory().constructCollectionType(List.class, PrescriptionRequestContent.PrescriptionDetail.class));
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }

            params.remove("prescription_detail");
        }

        PrescriptionRequestContent prescriptionRequestContent = JsonUtils.parseObject(params, PrescriptionRequestContent.class);

        prescriptionRequestContent.setPrescriptionDetail(prescriptionDetailList);

        return JsonUtils.toJsonString(prescriptionRequestContent);
    }

    /**
     * 构建微信验证实名接口入参
     *
     * @param accessToken
     * @param apiSecret
     * @param openid
     * @param username
     * @param idcard
     * @return
     */
    protected Map<String, String> buildWechatVerifyNameParameters(String accessToken, String apiSecret, String openid, String username, String idcard) {
        Map<String, String> result = new HashMap<>();
        result.put("access_token", accessToken);
        result.put("api_secret", apiSecret);

        result.put("openid", openid);
        result.put("name", username);
        result.put("idcard_md5", DigestUtils.md5Hex(idcard));

        result.put("nonce_str", RandomUtil.getRandomALLChar(32));

        return result;
    }

    /**
     * 验证平安支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkPacityPayParameters(Map<String, String> params) {
        if (StringUtils.isEmpty(params.get("app_id"))) return false;

        if (StringUtils.isEmpty(params.get("order_no"))) return false;
        if (StringUtils.isEmpty(params.get("open_id"))) return false;
        if (StringUtils.isEmpty(params.get("biz_scenario"))) return false;
        if (StringUtils.isEmpty(params.get("pay_form"))) return false;
        if (StringUtils.isEmpty(params.get("desc"))) return false;
        if (StringUtils.isEmpty(params.get("pay_amount"))) return false;
        if (StringUtils.isEmpty(params.get("return_url"))) return false;
        if (StringUtils.isEmpty(params.get("topic"))) return false;

        String payForm = params.get("pay_form");
        String bizScenario = params.get("biz_scenario");

        switch (Pacity.PayForm.valueOf(payForm)) {
            case MED_INSUR_FUNDED:
                if (StringUtils.isEmpty(params.get("his_order_no"))) return false;
                if (StringUtils.isEmpty(params.get("serial_no"))) return false;

                switch (Pacity.BizScenario.valueOf(bizScenario)) {
                    case INTER_AGENCY_PAY:
                        if (StringUtils.isEmpty(params.get("bill_no"))) return false;
                        break;
                    case HOSPITAL_REG:
                        if (StringUtils.isEmpty(params.get("yljgbm"))) return false;
                        if (StringUtils.isEmpty(params.get("ylzh"))) return false;
                        if (StringUtils.isEmpty(params.get("czybm"))) return false;
                        if (StringUtils.isEmpty(params.get("czyxm"))) return false;
                        if (StringUtils.isEmpty(params.get("mzlsh"))) return false;
                        if (StringUtils.isEmpty(params.get("ksbm"))) return false;
                        if (StringUtils.isEmpty(params.get("ksmc"))) return false;
                        if (StringUtils.isEmpty(params.get("ghlb"))) return false;
                        if (StringUtils.isEmpty(params.get("ghf"))) return false;
                        if (StringUtils.isEmpty(params.get("zjzj"))) return false;
                        if (StringUtils.isEmpty(params.get("ghhj"))) return false;
                        if (StringUtils.isEmpty(params.get("ssj"))) return false;
                        if (StringUtils.isEmpty(params.get("zsj"))) return false;
                        break;
                    default:
                        break;
                }
                break;
            default:
                break;
        }

        return true;
    }

    /**
     * 构建平安支付数据传输对象入参
     *
     * @return
     */
    protected PacityPayRequestDTO buildPacityPayRequestDTO(Map<String, String> params) throws Exception {
        String appId = params.get("app_id");
        PaymentSetting paymentSetting = getPaymentSetting(appId);

        PacityPayRequestDTO requestDTO = new PacityPayRequestDTO();
        requestDTO.setAccessToken(getPacityAccessToken());
        requestDTO.setPublicKey(paymentSetting.getPayPublicKey());
        requestDTO.setPrivateKey(paymentSetting.getPrivateKey());

        Date dateTradeStart = Calendar.getInstance().getTime();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(dateTradeStart);
        calendar.add(Calendar.DATE, 1);
        Date dateTradeExpire = calendar.getTime();

        requestDTO.setPlatMchId(paymentSetting.getMerchantAccount());
        requestDTO.setNonceStr(RandomUtil.getRandomALLChar(32));
        requestDTO.setPlatMchTradeNo(params.get("order_no"));
        requestDTO.setYztUserSecondId(params.get("open_id"));
        requestDTO.setBizScenario(params.get("biz_scenario"));
        requestDTO.setTradeType(Pacity.TRADE_CODE);
        requestDTO.setPayForm(params.get("pay_form"));
        requestDTO.setGoodsName(params.get("desc"));
        requestDTO.setTotalAmt(new BigDecimal(params.get("pay_amount")).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());
        requestDTO.setMchId(paymentSetting.getPartnerId());
        requestDTO.setDateTradeStart(DateUtils.dateToStringNoFlit(dateTradeStart));
        requestDTO.setDateTradeExpire(DateUtils.dateToStringNoFlit(dateTradeExpire));
        requestDTO.setNotifyUrl(getConfig(Config.PAY_PACITY_NOTIFY_URL));
        requestDTO.setResultPageUrl(params.get("return_url"));

        String payForm = params.get("pay_form");
        String bizScenario = params.get("biz_scenario");

        switch (Pacity.PayForm.valueOf(payForm)) {
            case MED_INSUR_FUNDED:
                params.put("is_insurance", "1");
                requestDTO.setMchTradeNo(params.get("his_order_no"));
                requestDTO.setMchBizSN(params.get("serial_no"));

                switch (Pacity.BizScenario.valueOf(bizScenario)) {
                    case INTER_AGENCY_PAY:
                        requestDTO.setMchBillNo(params.get("bill_no"));
                        break;
                    case HOSPITAL_REG:
                        requestDTO.setMessage(buildMedicarePayRegistrationRequestContent(params));
                        requestDTO.setMessageId(Pacity.MESSAGEID);
                        break;
                    default:
                        break;
                }
                break;
            default:
                break;
        }

        requestDTO.setMchEchoData(buildPayAttachData(params));

        requestDTO.setOrderTraceInfo(Pacity.ORDERTRACEINFO);
        requestDTO.setVersionNo(Pacity.API_VERSION);

        return requestDTO;
    }

    /**
     * 验证支付宝零元医保挂号参数
     *
     * @param params
     * @return
     */
    protected boolean checkAlipayMedInfoUploadParameters(Map<String, String> params) {
        return !StringUtils.isEmpty(params.get("app_id")) &&
                !StringUtils.isEmpty(params.get("order_no")) &&
                !StringUtils.isEmpty(params.get("openid")) &&
                !StringUtils.isEmpty(params.get("desc")) &&
                !StringUtils.isEmpty(params.get("org_code")) &&
                !StringUtils.isEmpty(params.get("org_name")) &&
                !StringUtils.isEmpty(params.get("serial_no")) &&
                !StringUtils.isEmpty(params.get("card_org_no")) &&
                !StringUtils.isEmpty(params.get("patient_card_type")) &&
                !StringUtils.isEmpty(params.get("patient_card_no")) &&
                !StringUtils.isEmpty(params.get("patient_name")) &&
                !StringUtils.isEmpty(params.get("patient_mobile")) &&
                !StringUtils.isEmpty(params.get("yljgbm")) &&
                !StringUtils.isEmpty(params.get("ylzh")) &&
                !StringUtils.isEmpty(params.get("czybm")) &&
                !StringUtils.isEmpty(params.get("czyxm")) &&
                !StringUtils.isEmpty(params.get("mzlsh")) &&
                !StringUtils.isEmpty(params.get("ksbm")) &&
                !StringUtils.isEmpty(params.get("ksmc")) &&
                !StringUtils.isEmpty(params.get("ghlb")) &&
                !StringUtils.isEmpty(params.get("ghf")) &&
                !StringUtils.isEmpty(params.get("zjzj")) &&
                !StringUtils.isEmpty(params.get("ghhj")) &&
                !StringUtils.isEmpty(params.get("ssj")) &&
                !StringUtils.isEmpty(params.get("zsj"));
    }

    /**
     * 构建支付宝零元医保挂号请求对象
     *
     * @param params
     * @return
     */
    protected AlipayRequest buildAlipayMedInfoUploadRequest(Map<String, String> params) throws PaySettingNotFoundException {
        String appId = params.get("app_id");
        PaymentSetting paymentSetting = getPaymentSetting(appId);

        AlipayRequest request = new AlipayRequest();
        request.setPayPublicKey(paymentSetting.getPayPublicKey());
        request.setPublicKey(paymentSetting.getPublicKey());
        request.setPrivateKey(paymentSetting.getPrivateKey());

        request.setAppId(appId);
        request.setMethod("alipay.commerce.medical.information.upload");
        request.setFormat("JSON");
        request.setCharset("utf-8");
        request.setSignType("RSA");
        request.setSign(null);
        request.setTimestamp(DateUtils.dateToString(Calendar.getInstance().getTime()));
        request.setVersion("1.0");
        request.setAppAuthToken(null);

        Map<String, Object> bizParams = new HashMap<>();
        bizParams.put("out_trade_no", params.get("order_no"));
        bizParams.put("scene", "REGISTRATION");
        bizParams.put("industry", "HOSPITAL");
        bizParams.put("buyer_id", params.get("openid"));
        bizParams.put("seller_id", paymentSetting.getPartnerId());
        bizParams.put("total_amount", "0");
        bizParams.put("subject", params.get("desc"));
        bizParams.put("org_no", getOrgNoByOrgCode(params.get("org_code")));
        bizParams.put("org_name", params.get("org_name"));
        bizParams.put("serial_no", params.get("serial_no"));
        bizParams.put("request_content", buildMedicarePayRegistrationRequestContent(params));
        bizParams.put("gmt_out_create", DateUtils.dateToString(Calendar.getInstance().getTime()));
        bizParams.put("patient_card_type", params.get("patient_card_type"));
        bizParams.put("patient_card_no", params.get("patient_card_no"));
        bizParams.put("patient_name", params.get("patient_name"));
        bizParams.put("patient_mobile", params.get("patient_mobile"));
        bizParams.put("is_insurance", "T");
        bizParams.put("medical_card_inst_id", params.get("card_org_no"));

        Map<String, String> extendParams = new HashMap<>();
        extendParams.put(Alipay.SYS_SERVICE_PROVIDER_ID, getConfig(Config.ALIPAY_SYS_SERVICE_PROVIDER_ID));
        bizParams.put("extend_params", extendParams);

        request.setBizContent(JSON.toJSONString(bizParams));

        return request;
    }

    /**
     * 验证河源一卡通支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkHyMedUnionParameters(Map<String, String> params) {
        return !StringUtils.isEmpty(params.get("app_code")) &&
                !StringUtils.isEmpty(params.get("org_code")) &&
                !StringUtils.isEmpty(params.get("insurance_login_name")) &&
                !StringUtils.isEmpty(params.get("order_no")) &&
                !StringUtils.isEmpty(params.get("pay_amount")) &&
                !StringUtils.isEmpty(params.get("pay_password"));
    }

    /**
     * 验证支付宝医疗自费WAP支付参数
     *
     * @param params
     * @return
     */
    protected boolean checkAlipayMedWapPayParameters(Map<String, String> params) {
        return StringUtils.isEmpty(params.get("app_id")) ||
                StringUtils.isEmpty(params.get("order_no")) ||
                StringUtils.isEmpty(params.get("pay_amount")) ||
                StringUtils.isEmpty(params.get("desc")) ||
                StringUtils.isEmpty(params.get("topic")) ||
                StringUtils.isEmpty(params.get("openid")) ||

                StringUtils.isEmpty(params.get("return_url")) ||
                StringUtils.isEmpty(params.get("scene")) ||
                StringUtils.isEmpty(params.get("industry"));
    }

    /**
     * 构建支付宝医疗自费WAP支付入参
     *
     * @param params
     * @return
     */
    protected Map<String, String> buildAlipayMedWapPayParameters(Map<String, String> params)
            throws PaySettingNotFoundException, AlipayApiException {
        String appId = params.get("app_id");
        PaymentSetting paymentSetting = getPaymentSetting(appId);
        String signType = "RSA";
        if (paymentSetting.getPayPublicKey().length() > 256) {
            signType = "RSA2";
        }

        Map<String, String> baseParams = new HashMap<>();

        baseParams.put("method", "alipay.commerce.medical.instcard.createandpay");
        baseParams.put("app_id", appId);
        baseParams.put("version", "1.0");
        baseParams.put("timestamp", DateUtils.dateToString(Calendar.getInstance().getTime()));
        baseParams.put("sign_type", signType);
        baseParams.put("return_url", params.get("return_url"));
        baseParams.put("notify_url", this.buildAlipayNotifyUrl(params));
        baseParams.put("sign_flag", "true");

        Map<String, String> businessParams = new HashMap<>();
        businessParams.put("out_trade_no", params.get("order_no"));
        businessParams.put("scene", params.get("scene"));
        businessParams.put("industry", params.get("industry"));
        businessParams.put("buyer_id", params.get("openid"));
        businessParams.put("seller_id", paymentSetting.getPartnerId());
        businessParams.put("total_amount",
                new BigDecimal(params.get("pay_amount")).divide(
                        new BigDecimal(100), 2, BigDecimal.ROUND_HALF_UP).toString());
        businessParams.put("subject", params.get("desc"));
        businessParams.put("timeout_express", "15m");

        businessParams.put("gmt_out_create", DateUtils.dateToString(Calendar.getInstance().getTime()));
        businessParams.put("is_insurance", "F");

        Map<String, String> extendParams = new HashMap<>();
        extendParams.put(Alipay.SYS_SERVICE_PROVIDER_ID, getConfig(Config.ALIPAY_SYS_SERVICE_PROVIDER_ID));
        businessParams.put("extend_params", JsonUtils.toJsonString(extendParams));

        Map<String, String> allParams = new HashMap<>();
        allParams.putAll(baseParams);
        allParams.putAll(businessParams);
        allParams.put("sign", this.generateAlipayMedicarePaySign(allParams, paymentSetting.getPrivateKey(), signType));

        return allParams;
    }

}
